package com.yun;

import java.io.*;
import java.nio.charset.Charset;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;

/**
 * Describe:
 * CreateUser: zoushuiyun
 * CreateTime: 2017/6/2
 */
public class GenerateBeansFromMysql {
    /**
     * 工具类使用说明。
     *
     * @author zoushuiyun
     */
    public static void usage() {
        StringBuffer usage = new StringBuffer(200);
        usage.append("************************************************************");
        usage.append("\n 根据数据库表自动生成对应的po，service，impl, mapper");
        usage.append("\n 使用参数举例如下：");
        usage.append("\n 该模块的包名，例如com.mass.test");
        usage.append("\n 数据库表名，例如Test");
        usage.append("\n 不需要生成的文件，例如service，可设置为service");
        usage.append("\n************************************************************");
        System.out.println(usage);
    }

    private final static String BASE = System.getProperty("user.dir");
    private final static String PROJECTNAME = "/springboot-jpa";//不同项目模板可能不同 区分之 template 中模板前缀
    private final static String JAVA = BASE + "/result/";


    /**
     *
     * @param args
     * @throws IOException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException {
        args = new String[]{"com.yunqian.digits", "v_operate_log_info", ""};
        if (args.length == 0) {
            usage();
            return;
        }
        String packageName = args[0];
        String dir = packageName.replaceAll("\\.", "/");

        String tableName = args[1];

        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://youipAddress:youPort/yuga?useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8mb4_bin", "you-user", "you-password");
        Statement statement = connection.createStatement();

        String tableComment = "";//表注释
        ResultSet resultSet = statement.executeQuery("SHOW CREATE TABLE "+tableName);
        if (resultSet != null && resultSet.next()) {
            String create = resultSet.getString(2);
            tableComment = parse(create);
        }

        DatabaseMetaData metaData = connection.getMetaData();
        ResultSet rs = metaData.getColumns(null, null, tableName, null);

        ArrayList<FieldColumn> fieldColumns = new ArrayList<FieldColumn>();
        while (rs.next()) {
            String columnName = rs.getString("COLUMN_NAME");
            FieldColumn fieldColumn = new FieldColumn();
            String propName = rs.getString("REMARKS");
            Integer columnLength = rs.getInt("COLUMN_SIZE");
            String columnType = rs.getString("TYPE_NAME");
            String isNullable = rs.getString("IS_NULLABLE");
            fieldColumn.setColumnName(columnName);
            fieldColumn.setJavaProperty(fieldToProperty(columnName));
            fieldColumn.setGetMethodName("get"+fieldToGsProperty(columnName));
            fieldColumn.setSetMethodName("set"+fieldToGsProperty(columnName));
//            if (columnName.toUpperCase().endsWith("ID")) {
//                fieldColumn.setJdbcType("String");
//            } else {
            fieldColumn.setJdbcType(getJdbcType(columnType));
//            }
            fieldColumn.setPropName(propName);
            fieldColumn.setColumnLength(columnLength);
            fieldColumn.setColumnType(getColumnType(columnType));
            fieldColumn.setRequired(!"YES".equals(isNullable));
            fieldColumn.setTableComment(tableComment);
            fieldColumns.add(fieldColumn);
        }
        if (fieldColumns.isEmpty()) {
            System.out.println("can not find table");
            System.exit(1);
        }
        String ignore = null;
        if (args.length == 3) {
            ignore = args[2];
        }

        String entityName = fieldToGsProperty(tableName);
//        ssm
//        populateMappers(tableName, packageName, fieldColumns, JAVA + dir + "\\po\\" +  entityName + "Mapper.xml");
//        if (ignore == null || !ignore.contains("po")) {
//            populatePo(tableName, fieldColumns, packageName, JAVA + dir + "\\po\\" + entityName + ".java",tableComment);
//        }
//        if (ignore == null || !ignore.contains("service")) {
//            populateService(tableName, packageName, JAVA + dir + "\\service\\" + entityName + "Service.java",tableComment);
//        }
//        if (ignore == null || !ignore.contains("impl")) {
//            populateServiceImpl(tableName, packageName, JAVA + dir + "\\service\\impl\\" + entityName + "ServiceImpl.java",tableComment);
//        }
//        if (ignore == null || !ignore.contains("controller")) {
//            populateController(tableName, packageName, JAVA + dir + "\\controller\\" + entityName + "Controller.java",tableComment);
//        }

//        springboot jpa
        if (ignore == null || !ignore.contains("domain")) {
            populateDomain(tableName, fieldColumns, packageName, JAVA + dir + "\\domain\\" + entityName + ".java",tableComment);
        }
        if (ignore == null || !ignore.contains("repository")) {
            populateRepository(tableName, fieldColumns, packageName, JAVA + dir + "\\repository\\" + entityName + "Repository.java",tableComment);
        }
        if (ignore == null || !ignore.contains("service")) {
            populateService(tableName, packageName, JAVA + dir + "\\service\\" + entityName + "Service.java",tableComment);
        }
        if (ignore == null || !ignore.contains("controller")) {
            populateController(tableName, packageName, JAVA + dir + "\\controller\\" + entityName + "Controller.java",tableComment);
        }
    }

    private static void populateDomain(String tableName, ArrayList<FieldColumn> fieldColumns, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(7);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", fieldToGsProperty(tableName));
        str = str.replaceAll("\\$\\{poUp\\}", fieldToGsProperty(tableName).toUpperCase());
        str = str.replaceAll("\\$\\{tableName\\}", tableName);
        String property = "";
        property += "\n";
        String getAndSet = "";
        String construstor = "";
        getAndSet +="\n";
        for (FieldColumn fieldColumn : fieldColumns) {
            if(fieldColumn.getJavaProperty().equals("id")||fieldColumn.getJavaProperty().equals("saveTime")||fieldColumn.getJavaProperty().equals("updateTime")||
                    fieldColumn.getJavaProperty().equals("updateKey")||fieldColumn.getJavaProperty().equals("userId")){
//               这几个在封装的继承类中有了，故在此注释 （框架ravan中有封装），使用其他框架时可根据框架自身特质做处理
                continue;
            }
            property += "    /**"+fieldColumn.getPropName()+"*/\n";
            property += "    @Column(name = \""+fieldColumn.getColumnName()+"\")\n";
            property += "    private " + fieldColumn.getJdbcType() + " " + fieldColumn.getJavaProperty() + ";\n";
//            getAndSet += "public " + fieldColumn.getJdbcType() + " " + fieldColumn.getGetMethodName() +
//                    "() { return " + fieldColumn.getJavaProperty() + "; }\n";
//            getAndSet += "public void " + fieldColumn.getSetMethodName() + "("+ fieldColumn.getJdbcType() + " " + fieldColumn.getJavaProperty()+") " +
//                    "{ this." + fieldColumn.getJavaProperty() + "="+fieldColumn.getJavaProperty()+"; }\n";
        }

        construstor = "public "+fieldToGsProperty(tableName)+"() {\n    }";
        str = str.replaceAll("\\$\\{property\\}", property);
        str = str.replaceAll("\\$\\{construstor\\}", construstor);
//        str = str.replaceAll("\\$\\{getAndSet\\}", getAndSet);
        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));

        writeFile(fileName,str);
    }

    private static void populateRepository(String tableName, ArrayList<FieldColumn> fieldColumns, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(8);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", fieldToGsProperty(tableName));
        str = str.replaceAll("\\$\\{poUp\\}", fieldToGsProperty(tableName).toUpperCase());
        str = str.replaceAll("\\$\\{tableName\\}", tableName);

        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));

        writeFile(fileName,str);
    }


    private static void populatePo(String tableName, ArrayList<FieldColumn> fieldColumns, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(1);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", fieldToGsProperty(tableName));
        str = str.replaceAll("\\$\\{tableName\\}", tableName);
        String property = "";
        property += "\n";
        String getAndSet = "";
        getAndSet +="\n";
        for (FieldColumn fieldColumn : fieldColumns) {
            if(fieldColumn.getJavaProperty().equals("id")||fieldColumn.getJavaProperty().equals("saveTime")||fieldColumn.getJavaProperty().equals("updateTime")||
                    fieldColumn.getJavaProperty().equals("updateKey")||fieldColumn.getJavaProperty().equals("userId")){
//               这几个在封装的继承类中有了，故在此注释 （框架ravan中有封装），使用其他框架时可根据框架自身特质做处理
                continue;
            }
            property += "    /**"+fieldColumn.getPropName()+"*/\n";
            property += "    private " + fieldColumn.getJdbcType() + " " + fieldColumn.getJavaProperty() + ";\n";
            getAndSet += "public " + fieldColumn.getJdbcType() + " " + fieldColumn.getGetMethodName() +
                    "() { return " + fieldColumn.getJavaProperty() + "; }\n";
            getAndSet += "public void " + fieldColumn.getSetMethodName() + "("+ fieldColumn.getJdbcType() + " " + fieldColumn.getJavaProperty()+") " +
                            "{ this." + fieldColumn.getJavaProperty() + "="+fieldColumn.getJavaProperty()+"; }\n";
        }
        str = str.replaceAll("\\$\\{property\\}", property);
        str = str.replaceAll("\\$\\{getAndSet\\}", getAndSet);
        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));

        writeFile(fileName,str);
    }
    private static void populateService(String tableName, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(4);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        String po = fieldToGsProperty(tableName);
        str = str.replaceAll("\\$\\{po\\}", po);

        str = str.replaceAll("\\$\\{poLow\\}", (new StringBuilder()).append(Character.toLowerCase(po.charAt(0))).append(po.substring(1)).toString());

        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        writeFile(fileName,str);
    }
    private static void populateServiceImpl(String tableName, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(5);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", fieldToGsProperty(tableName));

        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        writeFile(fileName,str);
    }

    private static void populateController(String tableName, String packageName, String fileName, String tableComment) throws IOException {
        String str = readFile(6);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", fieldToGsProperty(tableName));
        str = str.replaceAll("\\$\\{downPo\\}", fieldToProperty(tableName));

        str = str.replaceAll("\\$\\{author\\}", "zsy");
        str = str.replaceAll("\\$\\{tableComment\\}", tableComment);
        str = str.replaceAll("\\$\\{createTime\\}",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        writeFile(fileName,str);
    }

    private static void populateMappers(String tableName, String packageName, ArrayList<FieldColumn> fieldColumns, String fileName) throws IOException {
        String str = readFile(0);
        String result = "";
        String values = "";
        String column = "";
        String columnTable = "";
        String set = "";
        String whereCondition = "";
        int size = fieldColumns.size();
        int i = 0;
        for (FieldColumn fieldColumn : fieldColumns) {
            i++;
            String columnName = fieldColumn.getColumnName();
            String javaProperty = fieldColumn.getJavaProperty();
            if ("ID".equalsIgnoreCase(columnName)) {
                result += "<id property=\"";
                whereCondition += "<if test=\""+javaProperty+" != null\"> AND "+columnName+" = #{"+javaProperty+"}</if>\n";
            } else {
                result += "<result property=\"";
                values += ("#{" + javaProperty + ",jdbcType=" + fieldColumn.getColumnType() + "}");
                column += ("`" + columnName + "`");
                columnTable += (tableName + ".`" + columnName + "`");
                set += ("`" + columnName + "` = #{" + javaProperty + "}");
                whereCondition += "<if test=\""+javaProperty+" != null and "+javaProperty+" != ''\"> AND "+columnName+" = #{"+javaProperty+"}</if>";

                if (size != i) {
                    set += (",\n    ");
                    values += (",\n    ");
                    column += (",\n    ");
                    columnTable += (",\n    ");
                    whereCondition += ("\n    ");
                }
            }
            result += fieldColumn.getJavaProperty() + "\"" + " column=\"" + columnName + "\"" + " javaType=\"" + fieldColumn.getJdbcType() + "\" jdbcType=\"" + fieldColumn.getColumnType() + "\"/>\n";
        }
        String tableNameJava = fieldToGsProperty(tableName);
        str = str.replaceAll("\\$\\{package\\}", packageName);
        str = str.replaceAll("\\$\\{po\\}", tableNameJava);
        str = str.replaceAll("\\$\\{table\\}", tableName);
        str = str.replaceAll("\\$\\{result\\}", result);
        str = str.replaceAll("\\$\\{values\\}", values);
        str = str.replaceAll("\\$\\{updateColumn\\}", set);
        str = str.replaceAll("\\$\\{whereCondition\\}", whereCondition);
        str = str.replaceAll("\\$\\{tc\\}", column);
        str = str.replaceAll("\\$\\{tci\\}", columnTable);

        writeFile(fileName,str);
    }
    private static String parse(String all) {
        String comment = null;
        int index = all.indexOf("COMMENT='");
        if(index < 0) {
            return "";
        }
        comment = all.substring(index+9);
        comment = comment.substring(0,comment.length() - 1);
        return comment;
    }

    /**
     * 将数据库字段转换为java属性，如user_name-->userName
     *
     * @param field 字段名
     * @return
     */
    private static String fieldToProperty(String field) {
        if (null == field) {
            return "";
        }
        if(field.startsWith("t_")){
            field = field.substring(2);
        }
        if(field.startsWith("v_")){
            field = field.substring(2);
        }
        char[] chars = field.toLowerCase().toCharArray();
        StringBuffer property = new StringBuffer();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (c == '_') {
                int j = i + 1;
                if (j < chars.length) {
                    property.append(String.valueOf(chars[j]).toUpperCase());
                    i++;
                }
            } else {
                property.append(c);
            }
        }
        return property.toString();
    }


    /**
     * 将数据库字段转换为java属性，如user_name-->UserName
     *
     * @param field 字段名
     * @return
     */
    public static String fieldToGsProperty(String field) {
        if (null == field) {
            return "";
        }
        if(field.startsWith("t_")){
            field = field.substring(2);
        }

        if(field.startsWith("v_")){
            field = field.substring(2);
        }
        char[] chars = field.toLowerCase().toCharArray();
        StringBuffer property = new StringBuffer();
        for (int i = 0; i < chars.length; i++) {
            char c = chars[i];
            if (c == '_') {
                int j = i + 1;
                if (j < chars.length) {
                    property.append(String.valueOf(chars[j]).toUpperCase());
                    i++;
                }
            } else {
                if (i == 0) {
                    property.append(String.valueOf(chars[i]).toUpperCase());
                } else {
                    property.append(c);
                }
            }
        }
        return property.toString();
    }


    /**
     * @param type 0:mappers.xml
     * @return
     */
    private static String readFile(int type) throws IOException {
        String fileName = null;
        switch (type) {
            case 0: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/mappers.xml";
                break;
            }
            case 1: {
                fileName = BASE +"/template/mysql"+PROJECTNAME+"/po";
                break;
            }
            case 2: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/param";
                break;
            }
            case 3: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/dao";
                break;
            }
            case 4: {
                fileName = BASE+"/template/mysql"+PROJECTNAME+"/service";
                break;
            }
            case 5: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/impl";
                break;
            }
            case 6: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/controller";
                break;
            }
            case 7: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/domain";
                break;
            }
            case 8: {
                fileName = BASE + "/template/mysql"+PROJECTNAME+"/repository";
                break;
            }
        }
        if (fileName == null) {
            return null;
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), Charset.forName("UTF-8")));
        StringBuilder builder = new StringBuilder();
        String s;
        while ((s = reader.readLine()) != null) {
            builder.append(s).append("\n");
        }
        return builder.toString();
    }


    private static void writeFile(String fileName,String content) throws IOException {
        File file = new File(fileName);
        file.getParentFile().mkdirs();
        Writer writer = new OutputStreamWriter(new FileOutputStream(file),Charset.forName("UTF-8"));
        writer.write(content);
        writer.flush();
        writer.close();

    }

    public static final Map<String, String> typeMap;
    public static final Map<String, String> typeMap1;

    public static String getJdbcType(String columnType) {
        return typeMap.get(columnType);
    }

    public static String getColumnType(String columnType) {
        String s = typeMap1.get(columnType);
        return s == null ? columnType : s;
    }
    static {
        typeMap = new HashMap<String, String>();
        typeMap1 = new HashMap<String, String>();
        typeMap.put("VARCHAR", "String");
        typeMap.put("MEDIUMTEXT", "String");
        typeMap.put("CHAR", "Character");
        typeMap.put("SMALLINT", "Integer");
        typeMap.put("INT", "Integer");
        typeMap.put("TINYINT", "Integer");
        typeMap.put("FLOAT", "Float");
        typeMap.put("INT UNSIGNED", "Integer");
        typeMap.put("TEXT", "String");
        typeMap.put("BIGINT", "Long");
        typeMap.put("BOOLEAN", "Bollean");
        typeMap.put("TIMESTAMP", "Date");
        typeMap.put("DATETIME", "Date");
        typeMap.put("DECIMAL", "BigDecimal");


        typeMap1.put("INT", "INTEGER");
        typeMap1.put("MEDIUMTEXT", "VARCHAR");
        typeMap1.put("TEXT", "VARCHAR");
        typeMap1.put("BOOLEAN", "TINYINT");
        typeMap1.put("DATETIME", "TIMESTAMP");
    }


    static class FieldColumn {
        /**
         * 字段名
         */
        private String columnName;

        /**
         * 字段长度
         */
        private Integer columnLength;

        /**
         * 类属性
         */
        private String javaProperty;
        /**
         * 类属性名
         */
        private String propName;

        /**
         *属性get方法名
         */
        private String getMethodName;

        /**
         *属性set方法名
         */
        private String setMethodName;

        /**
         * 数据库类型
         */
        private String columnType;

        /**
         * java类型
         */
        private String jdbcType;

        /**
         * 是否必填
         */
        private Boolean required;

        /**
         * 表注释
         */
        private String tableComment;


        public String getColumnName() {
            return columnName;
        }

        public void setColumnName(String columnName) {
            this.columnName = columnName;
        }

        public Integer getColumnLength() {
            return columnLength;
        }

        public void setColumnLength(Integer columnLength) {
            this.columnLength = columnLength;
        }

        public String getJavaProperty() {
            return javaProperty;
        }

        public void setJavaProperty(String javaProperty) {
            this.javaProperty = javaProperty;
        }

        public String getPropName() {
            return propName;
        }

        public void setPropName(String propName) {
            this.propName = propName;
        }

        public String getGetMethodName() {
            return getMethodName;
        }

        public void setGetMethodName(String getMethodName) {
            this.getMethodName = getMethodName;
        }

        public String getSetMethodName() {
            return setMethodName;
        }

        public void setSetMethodName(String setMethodName) {
            this.setMethodName = setMethodName;
        }

        public String getColumnType() {
            return columnType;
        }

        public void setColumnType(String columnType) {
            this.columnType = columnType;
        }

        public String getJdbcType() {
            return jdbcType;
        }

        public void setJdbcType(String jdbcType) {
            this.jdbcType = jdbcType;
        }

        public Boolean getRequired() {
            return required;
        }

        public void setRequired(Boolean required) {
            this.required = required;
        }

        public String getTableComment() {
            return tableComment;
        }

        public void setTableComment(String tableComment) {
            this.tableComment = tableComment;
        }
    }
}
